'use strict';

const fs = require('fs')
const path = require('path')
const fse = require('fs-extra')
const inquirer = require('inquirer')
const glob = require('glob');
const ejs = require('ejs');
// 校验版本号
const semver = require('semver')
const userHome = require('user-home')
const Command = require('@imooc-cli-dev-dq/command')
const Package = require('@imooc-cli-dev-dq/package')
const {spinnerStart, sleep, execAsync} = require("@imooc-cli-dev-dq/utils")
const log = require('@imooc-cli-dev-dq/log')

const getProjectTemplate = require('./getProjectTemplate')

const TYPE_PROJECT = 'project'
const TYPE_COMPONENT = 'component'

const TEMPLATE_TYPE_NORMAL = 'normal';
const TEMPLATE_TYPE_CUSTOM = 'custom';

const WHITE_COMMAND = ['npm', 'cnpm'];
class InitCommand extends Command {
    init() {
        this.projectName = this._argv[0] || ''
        this.force = !!this._cmd.force
        log.verbose('projectName', this.projectName)
        log.verbose('force', this.force)
    }
    async exec() {
        try {
        // 1.准备阶段
        const projectInfo = await this.prepare()
        if (projectInfo) {
            log.verbose('projectInfo', projectInfo)
            this.projectInfo = projectInfo
            // 2.下载模板
            await this.downloadTemplate()
            // 3.安装模板
            await this.installTemplate()
        }
        } catch(e) {
            log.error(e.message)
        }
    }

    installTemplate() {
        log.verbose('templateInfo', this.templateInfo)
        if (this.templateInfo) {
            if (!this.templateInfo.type) {
                this.templateInfo.type = TEMPLATE_TYPE_NORMAL
            }
            if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
                // 标准安装
                console.log('标准安装！！')
                this.installNormalTemplate()
            } else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
                // 自定义安装
                console.log('自定义安装！！')
            } else {
                throw new Error('无法识别项目模板类型！')
            }
        } else {
            throw new Error('项目模板信息不存在！')
        }
    }
    checkCommand(cmd) {
        if (WHITE_COMMAND.includes(cmd)) {
            return cmd
        }
        return null
    }
    async execCommand(command, errMsg) {
        let ret;
        if (command) {
            const cmdArray = command.split(' ')
            const cmd = this.checkCommand(cmdArray[0])
            if (!cmd) {
                throw new Error('命令不存在！命令:' + command)
            }
            const args = cmdArray.slice(1)
            ret = await execAsync(cmd, args, {
                stdio: 'inherit',
                cwd: process.cwd()
            })
        }
        if (ret !== 0) {
            throw new Error(errMsg)
        }
        return ret
    }
    async ejsRender(options) {
        const dir = process.cwd()
        const projectInfo = this.projectInfo
        return new Promise((resolve, reject) => {
            glob('**', {
                cwd: dir,
                ignore: options.ignore || '',
                nodir: true
            }, function(err, files) {
                if (err) {
                    reject(err)
                }
                Promise.all(files.map(file => {
                    const filePath = path.join(dir, file)
                    return new Promise((resolve1, reject1) => {
                        ejs.renderFile(filePath, projectInfo, {}, (err, result) => {
                            if (err) {
                                reject1(err)
                            } else {
                                fse.writeFileSync(filePath, result)
                                resolve1(result)
                            }
                        })
                    })
                
                })).then(() => {
                    resolve()
                }).catch(err => reject(err))
            })
        })

    }
    async installNormalTemplate() {
        log.verbose('templateNpm', this.templateNpm)
        // 拷贝模板代码至当前目录
        let spinner = spinnerStart('正在安装模板...')
        await sleep()
        try {
            const templatePath = path.resolve(this.templateNpm.cacaeFilePath, 'template')
            // process.cwd() 方法返回 Node.js 进程的当前工作目录。
            const targetPath = process.cwd()
            fse.ensureDirSync(templatePath)
            fse.ensureDirSync(targetPath)
            fse.copySync(templatePath, targetPath)
        } catch(e) {
            throw e
        } finally {
            spinner.stop(true)
            log.success('模板安装成功！')
        }
        const templateIgnore = this.templateInfo.ignore || ['**/public/**']
        const ignore = ['**/node_modules/**', ...templateIgnore]
        await this.ejsRender({ignore})
        const {installCommand, startCommand} = this.templateInfo
        // 依赖安装
        await this.execCommand(installCommand, '依赖安装失败！')
        // 启动命令执行
        await this.execCommand(startCommand, '启动执行命令失败！')


    }
    async downloadTemplate() {
        // 1.通过项目模板api获取项目模板信息
        // 1.1.通过egg.js搭建一套后端系统
        // 1.2 通过npm存储项目模板
        // 1.3 将项目模板信息存储到mongodb数据库中
        // 1.4 通过egg.js获取mongodb的数据并且通过api返回
        const { projectTemplate} = this.projectInfo
        const templateInfo = this.template.find(item => item.npmName === projectTemplate)
        const targetPath = path.resolve(userHome, '.imooc-cli-dev', 'template')
        const storeDir = path.resolve(userHome, '.imooc-cli-dev', 'template', 'node-modules')
        const { npmName, version} = templateInfo
        this.templateInfo = templateInfo
        const templateNpm = new Package({
            targetPath,
            storeDir,
            packageName: npmName,
            packageVersion: version
        })
        log.verbose('templateNpm:', templateNpm)
        console.log(templateNpm.exists())
        if (!await templateNpm.exists()) {
            const spinner = spinnerStart('正在下载，请稍后')
            await sleep()
            try{
                await templateNpm.install()
            } catch (e) {
                throw e
            } finally {
                spinner.stop(true);
                if (await templateNpm.exists()) {
                    log.success('下载模板成功！')
                    this.templateNpm = templateNpm
                }
            }
        } else {
            const spinner = spinnerStart('正在更新，请稍后')
            await sleep()
            try{
                await templateNpm.update()
            } catch (e) {
                throw e
            } finally {
                spinner.stop(true);
                if (await templateNpm.exists()) {
                    log.success('更新模板成功！')
                    this.templateNpm = templateNpm
                }
            } 
        }
    }
    async prepare() {
        // 0. 判断项目模板是否存在
        const template = await getProjectTemplate()
        if (!template || template.length === 0) {
            throw new Error('项目模板不存在')
        }
        this.template = template
        // process.cwd() 方法返回 Node.js 进程的当前工作目录。
        const localPath = process.cwd()
        // 1. 判断当前目录是否为空
        if(!this.isDirEmpty(localPath)){
            let ifContinue = false
            if (!this.force) {
                // 1.1 询问当前目录是否继续创建
                ifContinue = (await inquirer.prompt({
                    type: 'confirm',
                    name: 'ifContinue',
                    default: false,
                    message: '当前文件夹不为空，是否继续创建项目？'
                })).ifContinue
                if (!ifContinue) {
                    return
                }
            }
            // 2. 是否强制更新
            if (ifContinue || this.force) {
                
                // 给i用户做二次确认
                const {confirmDelete} = await inquirer.prompt({
                    type: 'confirm',
                    name: 'confirmDelete',
                    default: false,
                    message: '是否确认清空当前目录下的文件？'
                })
                if (confirmDelete) {
                   

                    const spinner = spinnerStart('正在清空，请稍后')
                    try{
                         // 清空当前目录
                        await fse.emptyDirSync(localPath)
                    } catch (e) {
                        throw e
                    } finally {
                        spinner.stop(true);
                        // log.success('清空成功！')
                    }
                }
            }
        }
 
        return  this.getProjectInfo()
    }
    async getProjectInfo() {
        function isValidName(v) {
            return /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(v);
          }
        let projectInfo = {}
        let isProjectNameValid = false
        if (isValidName(this.projectName)) {
            isProjectNameValid = true
            projectInfo.projectName = this.projectName
        }
        // 1. 选择创建项目或组件
        const {type} = await inquirer.prompt({
            type: 'list',
            message: '请选择初始化类型',
            name: 'type',
            default: TYPE_PROJECT,
            choices: [{
                name: '项目',
                value: TYPE_PROJECT
            }, {
                name: '组件',
                value: TYPE_COMPONENT
            }]
        })
        const title = type === TYPE_PROJECT ? '项目' : '组件'
        const projectPrompt = []
        const projectNamePrompt = {
            type: 'input',
            message: `请输入${title}名称`,
            name: 'projectName',
            default: '',
            validate: function(v) {

                var done = this.async();

                // Do async stuff
                setTimeout(function() {
                  if (!/^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]|[a-zA-Z0-9])*$/.test(v)) {
                    // Pass the return value in the done callback
                    done('请输入正确格式的项目名称，如：（a, a-b, a_b, a-b_c, a-b1-c1,a_b1_c1）');
                    return;
                  }
                  // Pass the return value in the done callback
                  done(null, true);
                }, 0);
            },
            filter: function(v) {
                return v
            }
        }
        
        if (!isProjectNameValid) {
            projectPrompt.push(projectNamePrompt)
        }
        projectPrompt.push({
            type: 'input',
            message: `请输入${title}版本号`,
            name: 'projectVersion',
            default: '1.0.0',
            validate: function(v) {
                // return !!semver.valid(v)
                
                var done = this.async();

                // Do async stuff
                setTimeout(function() {
                  if (!semver.valid(v)) {
                    // Pass the return value in the done callback
                    done(`请输入正确格式的${title}版本号`);
                    return;
                  }
                  // Pass the return value in the done callback
                  done(null, true);
                }, 0);
            },
            filter: function(v) {
                if (!!semver.valid(v)) {
                    return semver.valid(v);
                  } else {
                    return v;
                  }
            }
        },
        {
            type: 'list',
            message: `请选择${title}模板`,
            name: 'projectTemplate',
            choices: this.createTemplateChoice()
        })
        if (type === TYPE_PROJECT) {
        
            // 2. 获取项目的基本信息
            const project = await inquirer.prompt(projectPrompt)
            projectInfo = {
                ...projectInfo,
                type,
                ...project
            }

        } else if (type === TYPE_COMPONENT) {

        }
        if (projectInfo.projectName) {
            projectInfo.name = projectInfo.projectName;
            projectInfo.className = require('kebab-case')(projectInfo.projectName).replace(/^-/, '');
        }
        if (projectInfo.projectVersion) {
          projectInfo.version = projectInfo.projectVersion;
        }
        if (projectInfo.componentDescription) {
          projectInfo.description = projectInfo.componentDescription;
        }
        return projectInfo
        

    }
    // 判断当前目录是否为空
    isDirEmpty(localPath) {
    
        // console.log(localPath)
        // console.log(path.resolve('.'))
        // console.log(__dirname)
        // 方法将返回一个包含“指定目录下所有文件名称”的数组对象。
        let fileList = fs.readdirSync(localPath)
        fileList = fileList.filter(file => (
            !file.startsWith('.') && ['node_modules'].indexOf(file) < 0
        )       
        )
        return !fileList || fileList.length <= 0
    }
    createTemplateChoice() {
        return this.template.map(item => ({
            value: item.npmName,
            name: item.name
        }))
    }
}

function init(argv) {
    return new InitCommand(argv)
}

module.exports = init
module.exports.InitCommand = InitCommand;

